package com.swak.reactivex.threads;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

import com.swak.reactivex.future.CompletableFutureTask;

/**
 * 定时任务...
 * 
 * @author 618lf
 */
class ScheduledFutureTask<V> extends CompletableFutureTask<V> implements java.util.concurrent.ScheduledFuture<V> {

	private static final long START_TIME = System.nanoTime();

	static long nanoTime() {
		return System.nanoTime() - START_TIME;
	}

	static long deadlineNanos(long delay) {
		long deadlineNanos = nanoTime() + delay;
		// Guard against overflow
		return deadlineNanos < 0 ? Long.MAX_VALUE : deadlineNanos;
	}
	
	static long deadlineToDelayNanos(long deadlineNanos) {
		return deadlineNanos == 0L ? 0L : Math.max(0L, deadlineNanos - nanoTime());
	}

	static long initialNanoTime() {
		return START_TIME;
	}

	static long validatePeriod(long period) {
		if (period == 0) {
			throw new IllegalArgumentException("period: 0 (expected: != 0)");
		}
		return period;
	}

	// set once when added to priority queue
	private long id;
	private long deadlineNanos;
	/* 0 - no repeat, >0 - repeat at fixed rate, <0 - repeat with fixed delay */
	private final long periodNanos;
	private final SingleThreadEventLoop eventLoop;

	ScheduledFutureTask(SingleThreadEventLoop eventLoop, Runnable runnable, long nanoTime) {
		super(runnable);
		this.eventLoop = eventLoop;
		deadlineNanos = nanoTime;
		periodNanos = 0;
	}

	ScheduledFutureTask(SingleThreadEventLoop eventLoop, Runnable runnable, long nanoTime, long period) {
		super(runnable);
		this.eventLoop = eventLoop;
		deadlineNanos = nanoTime;
		periodNanos = validatePeriod(period);
	}

	long deadlineNanos() {
		return deadlineNanos;
	}

	long delayNanos() {
		return deadlineToDelayNanos(deadlineNanos());
	}

	void setConsumed() {
		// Optimization to avoid checking system clock again
		// after deadline has passed and task has been dequeued
		if (periodNanos == 0) {
			assert nanoTime() >= deadlineNanos;
			deadlineNanos = 0L;
		}
	}

	ScheduledFutureTask<V> setId(long id) {
		if (this.id == 0L) {
			this.id = id;
		}
		return this;
	}

	@Override
	public long getDelay(TimeUnit unit) {
		return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
	}

	@Override
	public void run() {
		assert this.eventLoop.inEventLoop();
		try {
			if (delayNanos() > 0L) {
				// Not yet expired, need to add or remove from queue
				if (isCancelled()) {
					this.eventLoop.removeScheduled(this);
				} else {
					this.eventLoop.scheduleFromEventLoop(this);
				}
				return;
			}

			// 如果是只执行一次则设置执行结果
			if (periodNanos == 0) {
				if (!this.isDone() && !this.isCancelled()) {
					V result = runTask();
					this.complete(result);
				}
			}

			// 否则执行结束后重新设置下次需要的执行时间，重新设置到执行队列
			else {
				// check if is done as it may was cancelled
				if (!isCancelled()) {
					runTask();
					if (!this.eventLoop.isShutdown()) {
						if (periodNanos > 0) {
							deadlineNanos += periodNanos;
						} else {
							deadlineNanos = nanoTime() - periodNanos;
						}
						if (!isCancelled()) {
							this.eventLoop.scheduleFromEventLoop(this);
						}
					}
				}
			}
		} catch (Throwable cause) {
			this.completeExceptionally(cause);
		}
	}

	boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
		return super.cancel(mayInterruptIfRunning);
	}

	@Override
	public boolean cancel(boolean mayInterruptIfRunning) {
		boolean canceled = super.cancel(mayInterruptIfRunning);
		if (canceled) {
			this.eventLoop.removeScheduled(this);
		}
		return canceled;
	}

	@Override
	public int compareTo(Delayed o) {
		if (this == o) {
			return 0;
		}

		ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
		long d = deadlineNanos() - that.deadlineNanos();
		if (d < 0) {
			return -1;
		} else if (d > 0) {
			return 1;
		} else if (id < that.id) {
			return -1;
		} else {
			assert id != that.id;
			return 1;
		}
	}
}
